<?php

require_once __DIR__ . '/SymfonyRequirements.php';
require_once dirname(__FILE__) . '/../vendor/autoload.php';

use Symfony\Component\Process\ProcessBuilder;

/**
 * This class specifies all requirements and optional recommendations that are necessary to run the easycrm.
 */
class IcsocRequirements extends SymfonyRequirements
{
    const REQUIRED_PHP_VERSION  = '5.3.9';
    const REQUIRED_GD_VERSION   = '2.0';
    const REQUIRED_CURL_VERSION = '7.0';
    const REQUIRED_ICU_VERSION  = '3.8';

    const EXCLUDE_REQUIREMENTS_MASK = '/5\.3\.(3|4|8|16)|5\.4\.(0|8)/';

    public function __construct()
    {
        parent::__construct();

        $phpVersion  = phpversion();
        $gdVersion   = defined('GD_VERSION') ? GD_VERSION : null;
        $curlVersion = function_exists('curl_version') ? curl_version() : null;

        $this->addIcsocRequirement(
            version_compare($phpVersion, self::REQUIRED_PHP_VERSION, '>='),
            sprintf('PHP version must be at least %s (%s installed)', self::REQUIRED_PHP_VERSION, $phpVersion),
            sprintf(
                'You are running PHP version "<strong>%s</strong>", but Oro needs at least PHP "<strong>%s</strong>" to run.' .
                'Before using Oro, upgrade your PHP installation, preferably to the latest version.',
                $phpVersion,
                self::REQUIRED_PHP_VERSION
            ),
            sprintf('Install PHP %s or newer (installed version is %s)', self::REQUIRED_PHP_VERSION, $phpVersion)
        );

        $this->addIcsocRequirement(
            null !== $gdVersion && version_compare($gdVersion, self::REQUIRED_GD_VERSION, '>='),
            'GD extension must be at least ' . self::REQUIRED_GD_VERSION,
            'Install and enable the <strong>GD</strong> extension at least ' . self::REQUIRED_GD_VERSION . ' version'
        );

        $this->addIcsocRequirement(
            null !== $curlVersion && version_compare($curlVersion['version'], self::REQUIRED_CURL_VERSION, '>='),
            'cURL extension must be at least ' . self::REQUIRED_CURL_VERSION,
            'Install and enable the <strong>cURL</strong> extension at least ' . self::REQUIRED_CURL_VERSION . ' version'
        );

        $this->addIcsocRequirement(
            class_exists('ZMQ'),
            'ZMQ extension must be installed.',
            'Install and enable the <strong>ZMQ</strong> extension.'
        );

        $baseDir = realpath(__DIR__ . '/..');
        $mem     = $this->getBytes(ini_get('memory_limit'));

        $this->addPhpIniRequirement(
            'memory_limit',
            function ($cfgValue) use ($mem) {
                return $mem >= 512 * 1024 * 1024 || -1 == $mem;
            },
            false,
            'memory_limit should be at least 512M',
            'Set the "<strong>memory_limit</strong>" setting in php.ini<a href="#phpini">*</a> to at least "512M".'
        );

        $this->addIcsocRequirement(
            is_dir('/var/sounds'),
            '/var/sounds directory must be existed',
            'Create the directory of the "<strong>/var/sounds</strong>" so that the web server can write into it.'
        );

        $this->addIcsocRequirement(
            is_writable('/var/sounds'),
            '/var/sounds directory must be writable',
            'Change the permissions of the "<strong>/var/sounds</strong>" directory so that the web server can write into it.'
        );

        $this->addIcsocRequirement(
            is_dir('/var/record'),
            '/var/record directory must be existed',
            '创建目录"<strong>/var/record</strong>"，此目录是将录音服务器的目录通过nfs的方式挂载到此目录.'
        );

        $this->addIcsocRequirement(
            is_writable($baseDir . '/web/bundles'),
            'web/bundles/ directory must be writable',
            'Change the permissions of the "<strong>web/bundles/</strong>" directory so that the web server can write into it.'
        );
       
        if (is_dir($baseDir . '/web/js')) {
            $this->addIcsocRequirement(
                is_writable($baseDir . '/web/js'),
                'web/js directory must be writable',
                'Change the permissions of the "<strong>web/js</strong>" directory so that the web server can write into it.'
            );
        }

        if (is_dir($baseDir . '/web/css')) {
            $this->addIcsocRequirement(
                is_writable($baseDir . '/web/css'),
                'web/css directory must be writable',
                'Change the permissions of the "<strong>web/css</strong>" directory so that the web server can write into it.'
            );
        }

        if (!is_dir($baseDir . '/web/css') || !is_dir($baseDir . '/web/js')) {
            $this->addIcsocRequirement(
                is_writable($baseDir . '/web'),
                'web directory must be writable',
                'Change the permissions of the "<strong>web</strong>" directory so that the web server can write into it.'
            );
        }

        if (is_file($baseDir . '/app/config/parameters.yml')) {
            $this->addIcsocRequirement(
                is_writable($baseDir . '/app/config/parameters.yml'),
                'app/config/parameters.yml file must be writable',
                'Change the permissions of the "<strong>app/config/parameters.yml</strong>" file so that the web server can write into it.'
            );
        }

    }

    /**
     * Adds an Oro specific requirement.
     *
     * @param Boolean     $fulfilled Whether the requirement is fulfilled
     * @param string      $testMessage The message for testing the requirement
     * @param string      $helpHtml The help text formatted in HTML for resolving the problem
     * @param string|null $helpText The help text (when null, it will be inferred from $helpHtml, i.e. stripped from HTML tags)
     */
    public function addIcsocRequirement($fulfilled, $testMessage, $helpHtml, $helpText = null)
    {
        $this->add(new IcsocRequirement($fulfilled, $testMessage, $helpHtml, $helpText, false));
    }

    /**
     * Get the list of mandatory requirements (all requirements excluding PhpIniRequirement)
     *
     * @return array
     */
    public function getMandatoryRequirements()
    {
        return array_filter(
            $this->getRequirements(),
            function ($requirement) {
                return !($requirement instanceof PhpIniRequirement)
                    && !($requirement instanceof IcsocRequirement)
                    && !($requirement instanceof CliRequirement);
            }
        );
    }

    /**
     * Get the list of PHP ini requirements
     *
     * @return array
     */
    public function getPhpIniRequirements()
    {
        return array_filter(
            $this->getRequirements(),
            function ($requirement) {
                return $requirement instanceof PhpIniRequirement;
            }
        );
    }

    /**
     * Get the list of Oro specific requirements
     *
     * @return array
     */
    public function getIcsocRequirements()
    {
        return array_filter(
            $this->getRequirements(),
            function ($requirement) {
                return $requirement instanceof IcsocRequirement;
            }
        );
    }

    /**
     * @return array
     */
    public function getCliRequirements()
    {
        return array_filter(
            $this->getRequirements(),
            function ($requirement) {
                return $requirement instanceof CliRequirement;
            }
        );
    }

    /**
     * @param  string $val
     * @return int
     */
    protected function getBytes($val)
    {
        if (empty($val)) {
            return 0;
        }

        preg_match('/([\-0-9]+)[\s]*([a-z]*)$/i', trim($val), $matches);

        if (isset($matches[1])) {
            $val = (int)$matches[1];
        }

        switch (strtolower($matches[2])) {
            case 'g':
            case 'gb':
                $val *= 1024;
            // no break
            case 'm':
            case 'mb':
                $val *= 1024;
            // no break
            case 'k':
            case 'kb':
                $val *= 1024;
            // no break
        }

        return (float)$val;
    }

    /**
     * {@inheritdoc}
     */
    public function getRequirements()
    {
        $requirements = parent::getRequirements();

        /**
         * @var  $key
         * @var Requirement $requirement
         */
        foreach ($requirements as $key => $requirement) {
            $testMessage = $requirement->getTestMessage();
            if (preg_match_all(self::EXCLUDE_REQUIREMENTS_MASK, $testMessage, $matches)) {
                unset($requirements[$key]);
            }
        }

        return $requirements;
    }

    /**
     * {@inheritdoc}
     */
    public function getRecommendations()
    {
        $recommendations = parent::getRecommendations();

        /**
         * @var  $key
         * @var Requirement $recommendation
         */
        foreach ($recommendations as $key => $recommendation) {
            $testMessage = $recommendation->getTestMessage();
            if (preg_match_all(self::EXCLUDE_REQUIREMENTS_MASK, $testMessage, $matches)) {
                unset($recommendations[$key]);
            }
        }

        return $recommendations;
    }

    /**
     * @return bool
     */
    protected function checkFileNameLength()
    {
        $getConf = new ProcessBuilder(array('getconf', 'NAME_MAX', __DIR__));
        $getConf = $getConf->getProcess();

        if (isset($_SERVER['PATH'])) {
            $getConf->setEnv(array('PATH' => $_SERVER['PATH']));
        }
        $getConf->run();

        if ($getConf->getErrorOutput()) {
            // getconf not installed
            return true;
        }

        $fileLength = trim($getConf->getOutput());

        return $fileLength == 255;
    }

    protected function checkNodeJS()
    {
        $jsEngines = array('node', 'nodejs');
        $availableJsEngines = [];

        foreach ($jsEngines as $engine) {
            $jsExists = new ProcessBuilder(array($engine, '-help'));
            $jsExists = $jsExists->getProcess();
            if (isset($_SERVER['PATH'])) {
                $jsExists->setEnv(array('PATH' => $_SERVER['PATH']));
            }
            $jsExists->run();
            if ($jsExists->getErrorOutput() === null) {
                $availableJsEngines[] = $engine;
            }
        }

        return empty($availableJsEngines) ? false : true;
    }
}

class IcsocRequirement extends Requirement
{
}

class CliRequirement extends Requirement
{
    /**
     * @var string
     */
    protected $output;

    /**
     * @return string
     */
    public function getOutput()
    {
        return $this->output;
    }

    /**
     * @param string $output
     */
    public function setOutput($output)
    {
        $this->output = $output;
    }
}
